﻿// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

#include "heartrate-global.h"
#include "devicehandler.h"
#include "deviceinfo.h"

#include <QtEndian>
#include <QRandomGenerator>
#include <QFile>
#include <QDir>

uint16_t ModbusCRC16(QByteArray senddata);



DeviceHandler::DeviceHandler(QObject *parent) :
    BluetoothBaseClass(parent)
{
    if (simulator) {
        m_demoTimer.setSingleShot(false);
        m_demoTimer.setInterval(2000);
        connect(&m_demoTimer, &QTimer::timeout, this, &DeviceHandler::updateDemoHR);
        m_demoTimer.start();
        updateDemoHR();     //开始时，先更新一下。

        sta_ymodem= STA_IDLE;   //初始值。
        idx_pak= 0;
        m_update= false;

        timer_recv.setSingleShot(true);     //单次
        timer_recv.setInterval(5000);
        connect(&timer_recv, &QTimer::timeout, this, &DeviceHandler::timer_recv_handler);
    }

    m_txt_ok_v= false;
    client= new ClientThread(this);

    connect(client, &ClientThread::error, this, &DeviceHandler::client_err);
    connect(client, &ClientThread::TransOk, this, &DeviceHandler::client_trans_ok);
}


void DeviceHandler::setRatio(float val)
{
    m_ratio= val;

    emit ratioChanged();
}

void DeviceHandler::setAddressType(AddressType type)
{
    switch (type) {
    case DeviceHandler::AddressType::PublicAddress:
        m_addressType = QLowEnergyController::PublicAddress;
        break;
    case DeviceHandler::AddressType::RandomAddress:
        m_addressType = QLowEnergyController::RandomAddress;
        break;
    }
}

DeviceHandler::AddressType DeviceHandler::addressType() const
{
    if (m_addressType == QLowEnergyController::RandomAddress)
        return DeviceHandler::AddressType::RandomAddress;

    return DeviceHandler::AddressType::PublicAddress;
}

void DeviceHandler::setDevice(DeviceInfo *device)
{
    clearMessages();
    m_currentDevice = device;


    if (simulator) {
        setInfo(tr("Demo device connected."));
        return;
    }

    // Disconnect and delete old connection
    if (m_control) {
        m_control->disconnectFromDevice();  //QQ断开就这么干脆吗？-对。有关联的信号吗?-无。相关：enum QLowEnergyController::ControllerState。相应的是->connectToDevice()。
        delete m_control;
        m_control = nullptr;
    }

    // Create new controller and connect it if device available
    if (m_currentDevice) {

        // Make connections
        //! [Connect-Signals-1]
        m_control = QLowEnergyController::createCentral(m_currentDevice->getDevice(), this);    //QQ多个连接，就要有多个m_control吗？
        //! [Connect-Signals-1]
        m_control->setRemoteAddressType(m_addressType);
        //! [Connect-Signals-2]
        connect(m_control, &QLowEnergyController::serviceDiscovered,
                this, &DeviceHandler::serviceDiscovered);
        connect(m_control, &QLowEnergyController::discoveryFinished,
                this, &DeviceHandler::serviceScanDone);

        //手册上说，可能没有connectionUpdated信号。
        connect(m_control, &QLowEnergyController::mtuChanged,
                this, &DeviceHandler::connectionPara);

        connect(m_control, &QLowEnergyController::errorOccurred, this,
                [this](QLowEnergyController::Error error) {
                    Q_UNUSED(error);
                    setError("Cannot connect to remote device.");
                });
        connect(m_control, &QLowEnergyController::connected, this, [this]() {
            setInfo("Controller connected. Search services...");
            m_control->discoverServices();
        });
        connect(m_control, &QLowEnergyController::disconnected, this, [this]() {
            setError("LowEnergy controller disconnected");
        });

        // Connect
        m_control->connectToDevice();   //QQ怎么没有参数，那连接谁呢？A：实例化m_control时，已指定了QBluetoothDeviceInfo。
        //! [Connect-Signals-2]
    }
}


/*点击Boot.qml上“刷机”按钮，执行C++中的函数：发送重启报文。
 * 使用Y Modem协议升级过程，都是仪表是驱动。例，
 * 仪表定时发送'C'，APP收到后，发送第0包（128B），仪表再以ACK。
 * 仪表发送1个'C"，请求发送数据包，APP收到后，发送第1个数据包（1024B），之后再传输N个数据包（1024B）。
*/
void DeviceHandler::boot_start()
{
    if (!alive()) {
        LOGD("err: connect invalid");
    }

    if (nullptr == m_service) {
        LOGD("err: service invalid");
    }

    if(0 != client->bin_sta){
        //从远程服务器获取文件失败。
        qDebug()<< QString("err: no bin data");
    }

    /*输出获取到的数据的大小和文件名称，再放入QTemporaryFile文件。
    */
    bin_ba= client->get_ba_bin();
    setBin_name(client->get_bin_name());
    qDebug()<< QString("bin_ba len=")<< bin_ba.size()<< bin_name();

    bin_tmp_file.close();   //关闭作用：如果不关闭，那么下面的open()会累加文件大小。
    bin_tmp_file.open();
    qDebug()<< QString("bin_tmp_file name:")<< bin_tmp_file.fileName();

    //qDebug()<<QString("bin_tmp_file write()")<< bin_tmp_file.write(bin_ba)<< bin_ba.size();
    if(bin_ba.size() != bin_tmp_file.write(bin_ba)){
        //写入不对
        qDebug()<<QString("err:bin_tmp_file write()");
        return;
    }

    //实测，pos()返回文件大小，即在文件尾。isSequential()返回false。
    qDebug()<< QString("bin_tmp_file's pos=")<< bin_tmp_file.pos()<< "file_size"<< bin_tmp_file.size()<< QString("QTemporaryFils is sequential.=")<<bin_tmp_file.isSequential();
    bin_tmp_file.seek(0);


    m_start = QDateTime::currentDateTime();

    setRatio(0.0);
    setBtn_update(false);


    quint16 uuid_wr= 0xfff2;    //Write no response属性。
    //const QLowEnergyCharacteristic wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));
    wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));    //发现服务时，没有找这个特定的特性，而是已知特性。

    if (!wrChar.isValid()) {
        LOGD("err: wrChar invalid");
        return;
    }


    LOGD("MTU=%d", m_control->mtu());
    //LOGD("min=%d,max=%d,latency=%d",m_control->connectionUpdated())   //QQ
#ifdef BIN_FILE
    bin_file.setFileName("/sdcard/DT45G/"+m_binfilename);    //m_binfilename是全路径文件名。
    if(true != bin_file.open(QIODevice::ReadOnly)){
        LOGD("err: bin_file open"); //qDebug()<<"err: bin_file open";
        return;
    }

    LOGD("open file ok @%s", __FUNCTION__);

#endif

    sta_ymodem= STA_IDLE;
#ifdef BIN_FILE
    num_pak= (bin_file.size()+1023)/1024;
#else
    num_pak= (bin_tmp_file.size()+1023)/1024;
#endif
    idx_pak= 0;


    //m_ratio= (float)idx_pak;
    //m_ratio/= (float)num_pak;
    //emit ratioChanged();

    //m_update= false;
    //emit update_okChanged();

    setRatio((float)idx_pak/(float)num_pak);
    setUpdate(false);

    wrValue.clear();
    wrValue.append(0x01);
    wrValue.append(0x06);       //功能码：写单个寄存器。
    wrValue.append((char)0x0);  //寄存器地址
    wrValue.append(0x01);
    wrValue.append('B');        //寄存器数值,'B'-0x42,'T'-0x54.
    wrValue.append('T');
    quint16 crc16;
    crc16= ModbusCRC16(wrValue);
    wrValue.append(crc16);      //LSB first.
    wrValue.append(crc16>> 8);

    m_service->writeCharacteristic(wrChar, wrValue, QLowEnergyService::WriteWithoutResponse);

    //sta_ymodem= STA_CCC;  //因为发送复位命令，设备启动会有一些时间。所以，不主动进行状态转换，而在writeok()中进行状态转换。
    //timer_recv.start();
    setStat_tim(true);
}



void DeviceHandler::startMeasurement()
{
    if (alive()) {
        m_start = QDateTime::currentDateTime();
        m_min = 0;
        m_max = 0;
        m_avg = 0;
        m_sum = 0;
        m_calories = 0;
        m_measuring = true;
        m_measurements.clear();
        emit measuringChanged();    //QQ为什么发信号？仅仅是告之QML(!!QML中可以没有关联信号函数，可由QML属性机制完成），property bool measuring有了变化，QML更新UI。

        //WriteNoResponse.
        if (m_service) {
            quint16 uuid_wr= 0xfff2;    //Write no response属性。
            //const QLowEnergyCharacteristic wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));
            wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));

            if (!wrChar.isValid()) {
                LOGD("err:no wrChar");

                setTxt_color("red");
                setTxt_info("蓝牙连接已断开");
                setTxt_ok_v(true);          //显示提示信息。
    //            setTxt_info("蓝牙连接已断开");
    //            setV_txt_info_cal(true);   //显示提示信息。
    //            setV_txt_info_key(true);
    //            setV_txt_info_set(true);
    //            setV_txt_info_plc(true);
    //            setV_txt_info_laji(true);
            }
            else{
                LOGD("MTU=%d", m_control->mtu());
                //LOGD("min=%d,max=%d,latency=%d",m_control->connectionUpdated())

                bin_file.setFileName("/sdcard/DT45G/DT45G_08008000.bin");
                if(true != bin_file.open(QIODevice::ReadOnly)){
                    qDebug()<<"err: bin_file open";
                    return;
                }
                else{
                    LOGD("open file ok %s", __FUNCTION__);
                }

                sta_ymodem= STA_IDLE;
                num_pak= (bin_file.size()+1023)/1024;
                idx_pak= 0;

                //m_ratio= (float)idx_pak;
                //m_ratio/= (float)num_pak;
                //emit ratioChanged();
                setRatio((float)idx_pak/(float)num_pak);

                //m_update= false;
                //emit update_okChanged();
                setUpdate(false);

                wrValue.clear();
                wrValue.append(0x01);
                wrValue.append(0x06);       //功能码：写单个寄存器。
                wrValue.append((char)0x0);  //寄存器地址
                wrValue.append(0x01);
                wrValue.append('B');        //寄存器数值,'B'-0x42,'T'-0x54.
                wrValue.append('T');
                wrValue.append(0x02);
                wrValue.append(0xCC);

                m_service->writeCharacteristic(wrChar, wrValue, QLowEnergyService::WriteWithoutResponse);

                //sta_ymodem= STA_CCC;  //因为发送复位命令，设备启动会有一些时间。所以，不主动进行状态转换，而在writeok()中进行状态转换。
                //timer_recv.start();
            }
        }
    }
    else{
        LOGD("start wrChar");
    }
}

void DeviceHandler::stopMeasurement()
{
    m_measuring = false;
    emit measuringChanged();        //QQ该信号如何处理？没有connect，用了Q_PROPERTY机制：只需发射信号，QML自动更新UI。
}

//! [Filter HeartRate service 1]
void DeviceHandler::serviceDiscovered(const QBluetoothUuid &gatt)
{
    LOGD("gatt.uuid=%04x", gatt.toUInt16());

    //if (gatt == QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::HeartRate)) {
    quint16 uuid_com;
    uuid_com= 0xFFF0;
    if(gatt == QBluetoothUuid(uuid_com)) {
        setInfo("Heart Rate service discovered. Waiting for service scan to be done...");
        m_foundHeartRateService = true;
    }
}


//! [Filter HeartRate service 1]
void DeviceHandler::connectionPara(int mtu)
{
    LOGD("222mtu=%d", mtu);
}


void DeviceHandler::serviceScanDone()
{
    setInfo("Service scan done.");

    // Delete old service if available
    if (m_service) {
        delete m_service;
        m_service = nullptr;
    }

//! [Filter HeartRate service 2]
    // If heartRateService found, create new service
    if (m_foundHeartRateService){
        quint16 uuid_user;
        uuid_user= 0xFFF0;

        //m_service = m_control->createServiceObject(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::HeartRate), this);
        m_service = m_control->createServiceObject(QBluetoothUuid(uuid_user), this);

        LOGD("MTU=%d", m_control->mtu());
    }

    if (m_service) {    //发现了HeartRate Service。This signal(characteristicChanged) is emitted when the value of characteristic is changed by an event on the peripheral/device side.
        connect(m_service, &QLowEnergyService::stateChanged, this, &DeviceHandler::serviceStateChanged);
        connect(m_service, &QLowEnergyService::characteristicChanged, this, &DeviceHandler::updateHeartRateValue);  //暂认为使能特性的CCCD后，见上面一句注释。
        connect(m_service, &QLowEnergyService::descriptorWritten, this, &DeviceHandler::confirmedDescriptorWrite);
        connect(m_service, &QLowEnergyService::characteristicWritten, this, &DeviceHandler::writeok);
        m_service->discoverDetails();   //会有DiscoveryRequired --> DiscoveryService --> ServiceDiscoverd的2次transition，每次都emit stateChanged。
                                        //C 开启本服务的发现之旅，以发现服务下的包含服务，特性和相应的描述符（好像只有特性描述符）。
    } else {
        setError("Heart Rate Service not found.");
    }
//! [Filter HeartRate service 2]
}

// Service functions
//! [Find HRM characteristic]
void DeviceHandler::serviceStateChanged(QLowEnergyService::ServiceState s)
{
    switch (s) {
    case QLowEnergyService::RemoteServiceDiscovering:
        setInfo(tr("Discovering services..."));
        break;
    case QLowEnergyService::RemoteServiceDiscovered:
    {
        setInfo(tr("Service discovered."));
        quint16 uuid_user= 0xFFF1;  //

        //const QLowEnergyCharacteristic hrChar = m_service->characteristic(QBluetoothUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement));    //特性是分组属性：特性声明，数值和各种描述符。
        const QLowEnergyCharacteristic hrChar = m_service->characteristic(QBluetoothUuid(uuid_user));   //Returns the matching characteristic for uuid; otherwise an invalid characteristic.
        if (!hrChar.isValid()) {
            setError("HR Data not found."); //此服务下没有指定特性（uuid_user的特性）。
            break;
        }

        m_notificationDesc = hrChar.descriptor(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration);  //C Characteristic may contain none, one or more descriptor.
        if (m_notificationDesc.isValid())
            m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));    //得用服务去写，且有descriptorWritten信号，已关联到confirmedDescriptorWrite()。

        break;
    }
    default:
        //nothing for now
        break;
    }

    emit aliveChanged();
}
//! [Find HRM characteristic]

/*手机收到了BLE数据。
*/
//! [Reading value]
void DeviceHandler::updateHeartRateValue(const QLowEnergyCharacteristic &c, const QByteArray &value)
{
    static quint32 cnt_dat= 0;
    static quint8 cnt_C= 0;
    quint16 crc16;

    QByteArray datA;


    if(c.uuid().toUInt16() != 0xFFF1){  //非指定特性（自定义0xFFF1），直接退出，即忽略。
        return;
    }

    // ignore any other characteristic change -> shouldn't really happen though
    //if (c.uuid() != QBluetoothUuid(QBluetoothUuid::CharacteristicType::HeartRateMeasurement)) //非指定特性（SIG规定的心跳特性），直接退出，即忽略。
    //    return;
    //调试输出接收的数据信息：
    cnt_dat+= value.length();
    LOGD("recv:uuid=%04x, len=%" PRIdQSIZETYPE ", total=%d", c.uuid().toUInt16(), value.length(), cnt_dat);    //qsizetype类型的输出用“%" PRIdQSIZETYPE "”，value.length()是qsizetype类型。

    quint8 len= value.length();
    quint8 i= 0;


    //输出接收到的字节数据。
    if(STA_CURVE != sta_ymodem){
        while(len){
            LOGD("%d:%02x", i, value.at(i));

            i++;
            len--;
        }
    }
    else{
        //STA_CURVE下，只输出接收到的字节个数和已收总数。
        cnt_in_curve+= value.length();
        LOGD("len=%" PRIdQSIZETYPE ",total= %d", value.length(), cnt_in_curve);//value.at(0),value.at(1),value.at(2),value.at(3),value.at(4),value.at(5),value.at(6));
    }
//    if(1 == value.length())
//        LOGD("%02x", value.at(0));
//    else if(2 == value.length())
//        LOGD("%02x,%02x", value.at(0),value.at(1));
    //    if(64 == value.length()){
//        LOGD("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", value.at(0),value.at(1),value.at(2),\
//                                              value.at(10),value.at(20),value.at(30),\
//                                              value.at(40),value.at(50),value.at(60),value.at(63) );
//    }



    /*这里根据MCU侧的应答和当前状态，进行处理。
     * 状态转换分为2种：
     * a. phone发送数据包成功。
     * b. phone接收到数据。
    */
    switch (sta_ymodem){
    case STA_IDLE:
        qDebug()<<"recv lenth:"<< value.length();
        idx_pak= 0;
        //打开文件时，STA_IDLE --> STA_CCC。
        break;

    case STA_CCC:
        for(qint32 i= 0; i< value.length(); i++){
            if('C' == value.at(i))
                cnt_C++;
        }

        if(3 <=cnt_C){
            idx_pak= 0;
            cnt_C= 0;
            //发送第0包，开启超时定时器（5s），状态变为STA_ECHO。
            datA.clear();
            datA.append(0x01);      //SOH---128B数据包的开头。
            datA.append(idx_pak);
            datA.append(~idx_pak);
            //datA.append(bin_file.fileName().toStdString());
            datA.append(m_binfilename.toStdString());           //QQ对吗？
            datA.append((char)0x0); //文件名和数据大小之间有1个0x0。
        #ifdef BIN_FILE
            datA.append(QString::number(bin_file.size()).toStdString());
        #else
            datA.append(QString::number(bin_tmp_file.size()).toStdString());
        #endif

            datA.append(0x20);

            while((128+3) != datA.length())
                datA.append(0x1A);

            crc16= ModbusCRC16(datA);
            datA.append(crc16);         //CRC16的MSB
            datA.append(crc16>>8);      //QQY modem使用的CRC是modbus crc吗？
            //datA.append(0xCC);               //添加CRC的MSB。
            //datA.append(0xDD);               //添加CRC的LSB。

            m_service->writeCharacteristic(wrChar, datA, QLowEnergyService::WriteWithoutResponse);

            //sta_ymodem= STA_ECHO;
            //idx_pak++;

            //开启超时定时器。
            timer_recv.setInterval(5000);
            timer_recv.start();
        }
        break;

    case STA_0_ACK:
        if(1 == value.length()){
            if(ACK == value.at(0)){
                LOGD("recv 0's ACK, STA_0_AKC -> STA_1_C");

                sta_ymodem= STA_1_C;
            }
        }
        break;

    case STA_1_C:   //发送第1包（即第1个1024B数据包）。!!这个必须由接收仪器数据触发：即接收到仪表请求发送第1包（1024B）的'C'。
        if(1 == value.length()){
            if(0 == idx_pak){
                setStat_tim(false);     //当收到仪表发送的索要第1个1024B数据包时，停止Boot页面中进度条的闪。并设置进度条初始为0。
                setRatio(0.0);
            }

            if('C' == value.at(0)){
            #ifdef BIN_FILE
                wrValue= bin_file.read(1024);
            #else
                wrValue= bin_tmp_file.read(1024);
            #endif

                if(0 == wrValue.length()){
                    //文件已读完，本次发送EOT。QQ这种情况不正常，因为.bin文件为0。
                    wrValue.clear();
                    wrValue.append(0x04);      //EOT

                    LOGD("send EOT...");
                    m_service->writeCharacteristic(wrChar, wrValue, QLowEnergyService::WriteWithoutResponse);
                    sta_ymodem= STA_EOT_SEND;   //
                }
                else{
                    if(1024 != wrValue.length()){
                        //填充FF到1024B。
                        while(1024 != wrValue.length())
                            wrValue.append(0x1A);      //协议规定填充0x1A。

                        LOGD("append 0x1A");
                    }

                    //先添加头和CRC16，再发送数据包（L=1024)
                    idx_pak= 1;
                    wrValue.insert(0, ~idx_pak);
                    wrValue.insert(0, idx_pak);
                    wrValue.insert(0, 0x02);            //添加STX，IDX，~IDX

                    crc16= ModbusCRC16(wrValue);
                    wrValue.append(crc16);         //CRC16的MSB
                    wrValue.append(crc16>>8);      //QQY modem使用的CRC是modbus crc吗？
                    //wrValue.append(0xCC);               //添加CRC的MSB。
                    //wrValue.append(0xDD);               //添加CRC的LSB。



                    datA= wrValue.left(197);            //QQ为什么取左边的197字节呢？想起来了，BLE发送需要满足MTU（200），不能超过这个。发送一个数据包（1024B），需要发送几次，每次发送197B。
                    //qDebug()<< "send cnt:"<<wrValue.length()<<"datA len:"<< datA.length();
                    LOGD("writeto:idx= %d, len= %" PRIdQSIZETYPE ", total= %" PRIdQSIZETYPE "", idx_pak, datA.length(), wrValue.length());

                    m_service->writeCharacteristic(wrChar, datA, QLowEnergyService::WriteWithoutResponse);
                }
                //idx_pak++;
            }
        }
        else{
            LOGD("err: NOT 'C'");
        }

        break;

    case STA_N_ACK: //发送后续的1024B数据包。
        if(1 == value.length()){
            if(ACK == value.at(0)){
            #ifdef BIN_FILE
                wrValue= bin_file.read(1024);
            #else
                wrValue= bin_tmp_file.read(1024);
            #endif

                if(0 == wrValue.length()){
                    //文件已读完，本次发送EOT
                    wrValue.clear();
                    wrValue.append(0x04);      //EOT

                    LOGD("send EOT...");
                    m_service->writeCharacteristic(wrChar, wrValue, QLowEnergyService::WriteWithoutResponse);
                    sta_ymodem= STA_EOT_SEND;    //需要在发送完成后，再转换状态。
                }
                else{
                    if(1024 != wrValue.length()){
                        //填充FF到1024B。
                        while(1024 != wrValue.length())
                            wrValue.append(0x1A);      //协议规定填充0x1A。

                        LOGD("append 0x1A");
                    }

                    //先添加头和CRC16，再发送数据包（L=1024)
                    wrValue.insert(0, ~idx_pak);
                    wrValue.insert(0, idx_pak);
                    wrValue.insert(0, 0x02);            //添加STX，IDX，~IDX

                    crc16= ModbusCRC16(wrValue);
                    wrValue.append(crc16);              //CRC16的MSB
                    wrValue.append(crc16>>8);           //QQY modem使用的CRC是modbus crc吗？
                    //wrValue.append(0xCC);             //添加CRC的MSB。
                    //wrValue.append(0xDD);             //添加CRC的LSB。

                    datA= wrValue.left(197);
                    //qDebug()<< "send cnt:"<<wrValue.length()<<"datA len:"<< datA.length();
                    LOGD("idx= %d, len= %" PRIdQSIZETYPE ", total= %" PRIdQSIZETYPE "", idx_pak, datA.length(), wrValue.length());

                    m_service->writeCharacteristic(wrChar, datA, QLowEnergyService::WriteWithoutResponse);
                }
                //idx_pak++;
            }
            else{
                LOGD("err1 echo in STA_N_ACK");
            }
        }
        else{
            LOGD("err2 echo in STA_N_ACK");
        }
        break;

    case STA_EOT_ACK:
        if(1 == value.length()){
            if(ACK == value.at(0)){
                //收到'C'，发送全0数据包（类比第0包，是128B，非1024B）。
                wrValue.clear();
                if(128 != wrValue.length()){
                    //填充FF到128B。
                    while(128 != wrValue.length())
                        wrValue.append((char)0x0);      //协议规定填充0x1A。
                }

                //先添加头和CRC16，再发送数据包（L=1024)
                idx_pak= 0;
                wrValue.insert(0, ~idx_pak);
                wrValue.insert(0, idx_pak);
                wrValue.insert(0, 0x01);           //添加STX，IDX，~IDX

                crc16= ModbusCRC16(wrValue);
                wrValue.append(crc16);         //CRC16的MSB
                wrValue.append(crc16>>8);      //QQY modem使用的CRC是modbus crc吗？
                //wrValue.append(0xCC);              //添加CRC的MSB。
                //wrValue.append(0xDD);              //添加CRC的LSB

                LOGD("send all 0...");
                datA= wrValue.left(128+5);
                m_service->writeCharacteristic(wrChar, datA, QLowEnergyService::WriteWithoutResponse);

                //sta_ymodem= STA_000_ECHO;
            }
        }
        else{
            LOGD("err echo in STA_EOT_ACK");
        }
        break;

    case STA_000_ACK:
        if((1 == value.length()) &&
           (ACK == value.at(0)) ){
                //收到对全0数据包的应答，即传输完成。
                sta_ymodem= STA_IDLE;
                idx_pak= 0;

                cnt_C= 0;

                m_update= true;
                emit update_okChanged();    //一个文件传输完，并等到了ACK。
                setTxt_ok_v(true);

                LOGD("file trans completed.");      //QQ输出换行无效，不支持"\r\n"。若写上，会输出多余信息。
                LOGD(" ");  //实测输出："11-29 14:26:05.994  2641  2719 D QtDebug :"
                LOGD(" ");  //实测输出："11-29 14:26:05.994  2641  2719 D QtDebug :"
        }
        else{
            LOGD("err echo in STA_000_ACK");
        }
        break;

    case STA_03H:   //等待modbus 03h的响应：这里只提现：正常响应，错误响应，不能体现：无响应。
        /*先判断功能码是否为错误码（即90h)
        */
        if(0x03 == value.at(1)){
            //正常响应
            uint16_t addr;
            addr= value.at(2);  //addr暂用于保存字节长度。

            if(addr == 4){
                LOGD("echo 03h ok, addr=%d", m_addr);    //字节数满足，就认为ok。

                //更新数据，同步到QML。
                if(m_addr == addr_laji_curve){
                    setLaji_curve(value.at(6));
                }
                else if(m_addr == addr_com_plc_addr){
                    setPlc_addr(value.at(6));
                }
                else if(m_addr == addr_com_plc_mode_idx){
                    setPlc_protocol_idx(value.at(6));
                }
                else if(m_addr == addr_com_plc_baud_idx){
                    setPlc_baud_idx(value.at(6));
                }
                else if(m_addr == addr_com_plc_format_idx){
                    setPlc_fmt_idx(value.at(6));
                }
                else if(m_addr == addr_com_interval_idx){
                    setPlc_interval_idx(value.at(6));
                }
                else if(m_addr == addr_rf_ch){
                    setRf_ch_idx(value.at(6));          //E610信道。
                }
                else if(m_addr == addr_zero_auto_sw_idx){
                    setZero_auto_sw_idx(value.at(6));
                }
                else if(m_addr == addr_range_zero_trace_idx){
                    setRange_zero_trace_idx(value.at(6));
                }
                else if(m_addr == addr_range_stab_idx){
                    setRange_stab_idx(value.at(6));
                }
                else if(m_addr == addr_range_zero){
                    setRange_zero(value.at(6));
                }
                else if(m_addr == addr_filter_dig_idx){
                    setFilter_dig_idx(value.at(6));
                }
                else if(m_addr == addr_filter_stab_idx){
                    setFilter_stab_idx(value.at(6));
                }
                else if(m_addr == addr_sps_idx){
                    setSps_idx(value.at(6));
                }
                else if(m_addr == addr_in1_define_idx){
                    setIn1_define_idx(value.at(6));
                }
                else if(m_addr == addr_out1_define_idx){
                    setOut1_define_idx(value.at(6));
                }
                else if(m_addr == addr_out2_define_idx){
                    setOut2_define_idx(value.at(6));
                }
                else if(m_addr == addr_laji_gap_min_idx){
                    setLaji_gap_min_idx(value.at(6));
                }
                else if(m_addr == addr_laji_gap_max_idx){
                    setLaji_gap_max_idx(value.at(6));
                }
                else if(m_addr == addr_laji_wt_hold_tmo_idx){
                    setLaji_hold_tmo_idx(value.at(6));
                }
                else if(m_addr == addr_lajitong_sample_cnt_idx){
                    setLajitong_sample_cnt_idx(value.at(6));
                }
                else if(m_addr == addr_laji_gps_gap){
                    setLaji_gps_gap(value.at(6));           //按1B处理，实际是4B（int）。
                }
                else if(m_addr == addr_laji_zzone_val){
                    setLaji_zzone_val(value.at(6));         //按1B处理，实际是4B（int）
                }
                else if(m_addr == addr_laji_zzone_tmo){
                    setLaji_zzone_tmo(value.at(6));         //按1B处理，实际是4B（int）
                }
                else if(m_addr == addr_cal_pt_idx){
                    setCal_pt_idx(value.at(6));
                }
                else if(m_addr == addr_cal_fd_idx){
                    setCal_fd_idx(value.at(6));
                }
                else if(m_addr == addr_cal_cp){
                    uint32_t tmp32;
                    tmp32= value.at(3);
                    tmp32<<= 8;
                    tmp32|= value.at(4);
                    tmp32<<= 8;
                    tmp32|= value.at(5);
                    tmp32<<= 8;
                    tmp32|= value.at(6);

                    setCal_cp(tmp32);
                }
                else if(m_addr == addr_cal_pwd){
                    uint32_t tmp32;
                    tmp32= value.at(3);
                    tmp32<<= 8;
                    tmp32|= value.at(4);
                    tmp32<<= 8;
                    tmp32|= value.at(5);
                    tmp32<<= 8;
                    tmp32|= value.at(6);

                    setCal_pwd(tmp32);
                }
                else if(m_addr == addr_cal_t){
                    uint32_t tmp32;
                    tmp32= value.at(3);
                    tmp32<<= 8;
                    tmp32|= value.at(4);
                    tmp32<<= 8;
                    tmp32|= value.at(5);
                    tmp32<<= 8;
                    tmp32|= value.at(6);

                    setCal_t(tmp32);
                }
                else if(m_addr == addr_cal_1){
                    uint32_t tmp32;
                    tmp32= value.at(3);
                    tmp32<<= 8;
                    tmp32|= value.at(4);
                    tmp32<<= 8;
                    tmp32|= value.at(5);
                    tmp32<<= 8;
                    tmp32|= value.at(6);

                    setCal_load1(tmp32);
                }
                else if(m_addr == addr_cal_2){
                    uint32_t tmp32;
                    tmp32= value.at(3);
                    tmp32<<= 8;
                    tmp32|= value.at(4);
                    tmp32<<= 8;
                    tmp32|= value.at(5);
                    tmp32<<= 8;
                    tmp32|= value.at(6);

                    setCal_load2(tmp32);
                }
                else if(m_addr == addr_cal_3){
                    uint32_t tmp32;
                    tmp32= value.at(3);
                    tmp32<<= 8;
                    tmp32|= value.at(4);
                    tmp32<<= 8;
                    tmp32|= value.at(5);
                    tmp32<<= 8;
                    tmp32|= value.at(6);

                    setCal_load3(tmp32);
                }
                else if(m_addr == addr_cal_4){
                    uint32_t tmp32;
                    tmp32= value.at(3);
                    tmp32<<= 8;
                    tmp32|= value.at(4);
                    tmp32<<= 8;
                    tmp32|= value.at(5);
                    tmp32<<= 8;
                    tmp32|= value.at(6);

                    setCal_load4(tmp32);
                }
                else{
                    //nothing.
                }

                setTxt_color("green");
                setTxt_info("成功");
                setTxt_ok_v(true);          //显示提示信息。
//                setV_txt_info_cal(true);   //显示提示信息。
//                setV_txt_info_key(true);
//                setV_txt_info_set(true);
//                setV_txt_info_plc(true);
//                setV_txt_info_laji(true);
            }
            else if((30*2) == addr){
                //数据长度为60（30个寄存器），且地址是版本信息。
                if(m_addr == addr_ver){
                    QByteArray array1= value.mid(3, 50); //value.at(3)开始的字符串。
                    QByteArray array2;
                    QString str1;
                    QString str2;
                    QString str3;

                    //硬件版本+软件版本+编译时间。
                    for(int i= 0; i< 18;){
                        array2.append(value.at(3+ i+1));
                        array2.append(value.at(3+ i));
                        i+= 2;
                    }
                    array2.append('\0');

                    str2.append(array2);
                    qDebug()<< str2;

                    //版本说明
                    array2.clear();
                    for(int i= 0; i< 32;){
                        array2.append(value.at(3+18+ i+1));
                        array2.append(value.at(3+18+ i));
                        i+= 2;
                    }
                    array2.append('\0');

                    str1.append(array2);
                    qDebug()<< str1;

                    //BOOT版本
                    array2.clear();
                    for(int i= 0; i< 8;){
                        array2.append(value.at(3+18+32+ i+1));
                        array2.append(value.at(3+18+32+ i));
                        i+= 2;
                    }
                    array2.append('\0');

                    str3.append(array2);
                    qDebug()<< str1;

                    //更新页面。
                    setVer_info(str2);
                    setVer_info_2(str1);
                    setVer_boot(str3);
                }
            }
        }
        else if(0x03 == value.at(1)){
            //错误响应
            setTxt_color("red");
            setTxt_info("失败");
            setTxt_ok_v(true);          //显示提示信息。
//            setV_txt_info_cal(true);   //显示提示信息。
//            setV_txt_info_key(true);
//            setV_txt_info_set(true);
//            setV_txt_info_plc(true);
//            setV_txt_info_laji(true);
        }
        else{
            //未知响应
            LOGD("err:unkown echo for 03h");
        }

        break;

    case STA_10H:   //等待modbus 10h的响应：这里只体现：正常响应，错误响应，不能体现：无响应。如果有响应，界面有显示（成功，失败），否则“无响应”对应没有显示。
        /*先判断功能码是否为错误码（即90h)
        */
        if(0x10 == value.at(1)){
            //正常响应
            uint16_t addr;
            addr= value.at(2);
            addr<<= 8;
            addr|= value.at(3);

            if(addr == m_addr){
                LOGD("echo 10h ok");    //地址相等，就认为ok。

                setTxt_color("green");
                setTxt_info("成功");
                setTxt_ok_v(true);          //显示提示信息。

//                setTxt_info("成功");
//                setV_txt_info_cal(true);
//                setV_txt_info_key(true);
//                setV_txt_info_set(true);
//                setV_txt_info_plc(true);
//                setV_txt_info_laji(true);

                if(addr == addr_laji_curve){
                    sta_ymodem= STA_CURVE;
                    cnt_in_curve= 0;    //总字节数清零。
                }

                if(addr == addr_cal_pwd){
                    //
                    setPwd_ok(true);    //允许Cal.qml界面下的各个“写入”按钮。
                }
            }
        }
        else if(0x90 == value.at(1)){
            //错误响应
            setTxt_color("red");
            setTxt_info("失败");
            setTxt_ok_v(true);          //显示提示信息。

            //setPwd_ok(false);


//            setTxt_info("失败");
//            setV_txt_info_cal(true);
//            setV_txt_info_key(true);
//            setV_txt_info_set(true);
//            setV_txt_info_plc(true);
//            setV_txt_info_laji(true);
        }
        else{
            //未知响应
            LOGD("err:unkown echo for 10h");
        }
        break;

    default:
        break;
    }

//    auto data = reinterpret_cast<const quint8 *>(value.constData());
//    quint8 flags = *data;

//    //Heart Rate
//    int hrvalue = 0;
//    if (flags & 0x1) // HR 16 bit? otherwise 8 bit
//        hrvalue = static_cast<int>(qFromLittleEndian<quint16>(data[1]));
//    else
//        hrvalue = static_cast<int>(data[1]);

//    addMeasurement(hrvalue);
}


//! [Reading value]
void DeviceHandler::timer_recv_handler()
{
    qDebug()<<"timer recv TMO";
}


void DeviceHandler::updateDemoHR()
{
    int randomValue = 0;
    if (m_currentValue < 30) { // Initial value
        randomValue = 55 + QRandomGenerator::global()->bounded(30);
    } else if (!m_measuring) { // Value when relax
        randomValue = qBound(55, m_currentValue - 2 + QRandomGenerator::global()->bounded(5), 75);
    } else { // Measuring
        randomValue = m_currentValue + QRandomGenerator::global()->bounded(10) - 2;
    }

    addMeasurement(randomValue);
}

void DeviceHandler::confirmedDescriptorWrite(const QLowEnergyDescriptor &d, const QByteArray &value)
{
    if (d.isValid() && d == m_notificationDesc && value == QByteArray::fromHex("0000")) {   //value值为"0000"，不是"0100"（只有0，1两种值），说明写失败。
        //disabled notifications -> assume disconnect intent（假定是断联了）
        m_control->disconnectFromDevice();
        delete m_service;
        m_service = nullptr;

        LOGD("err:CCCD write");
    }
    else {
        LOGD("ok:CCCD write");
        //进入Measure界面，BLE流程完成，
        m_ratio= 0;
        emit ratioChanged();

        m_update= false;
        emit update_okChanged();
    }
}


/*是characteristicWritten的槽函数。
 * This signal is emitted when the value of characteristic is successfully changed to newValue.
 * The change must have been triggered by calling writeCharacteristic().
 * If the write operation is not successful, the errorOccurred() signal is emitted using the CharacteristicWriteError flag.
*/
void DeviceHandler::writeok(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
{
    LOGD("writeok:%04x, idx= %d, len=%" PRIdQSIZETYPE "", characteristic.uuid().toUInt16(), idx_pak, newValue.length()); //此调试信息可看出1个1024B数据包是按每包197B，多次发送的。

    qsizetype pos= newValue.length();


    if(STA_KEY == sta_ymodem){
        LOGD("key send ok");
        return;
    }

    if(STA_03H == sta_ymodem){
        LOGD("pak 03h send ok");

        setTxt_ok_v(false);         //隐藏提示信息。
//        setV_txt_info_cal(false);   //隐藏提示信息。
//        setV_txt_info_key(false);
//        setV_txt_info_set(false);
//        setV_txt_info_plc(false);
//        setV_txt_info_laji(false);

        return;
    }

    if(STA_10H == sta_ymodem){
        LOGD("pak 10h send ok");

        setTxt_ok_v(false);         //隐藏提示信息。
//        setV_txt_info_cal(false);   //隐藏提示信息。
//        setV_txt_info_key(false);
//        setV_txt_info_set(false);
//        setV_txt_info_plc(false);
//        setV_txt_info_laji(false);

        return;
    }


    //下面的状态用于boot。
    if(STA_IDLE == sta_ymodem){
        //点击APP上的“刷机”按钮，发送重启报文。发送成功后，状态变化。
        sta_ymodem= STA_CCC;
        LOGD("STA_IDLE --> STA_CCC");

        return;
    }

    if(STA_CCC == sta_ymodem){
        //发送第0包成功，状态转换。
        sta_ymodem= STA_0_ACK;
        LOGD("STA_CCC --> STA_0_ACK");
        //idx_pak++;

        return;
    }


//    if(STA_0_ACK == sta_ymodem){
//        sta_ymodem= STA_N_ACK;
//        LOGD("STA_1_C --> STA_N_ACK");

//        return;
//    }


    if(STA_1_C == sta_ymodem){  //发送第1个数据包（1024B），需要发送几次。
        //1024B的数据包。
        wrValue= wrValue.sliced(pos);   //wrValue是1024B数据包。

        if(0 == wrValue.length()){
            LOGD("%d pak(1024B) send ok", idx_pak);
            //m_ratio= (float)idx_pak;
            //m_ratio/= (float)num_pak;
            //emit ratioChanged();
            setRatio((float)idx_pak/(float)num_pak);

            sta_ymodem= STA_N_ACK;
            LOGD("STA_1_C --> STA_N_ACK");
            idx_pak++; 
        }
        else{
            QByteArray sndArray= wrValue.left(197);

            m_service->writeCharacteristic(wrChar, sndArray, QLowEnergyService::WriteWithoutResponse);
        }

        return;
    }


    if(STA_N_ACK == sta_ymodem){
        wrValue= wrValue.sliced(pos);   //wrValue是1024B数据包。

        if(0 == wrValue.length()){
            LOGD("%d pak(1024B) send ok", idx_pak);
            setRatio((float)idx_pak/(float)num_pak);
//            m_ratio= (float)idx_pak;
//            m_ratio/= (float)num_pak;

            idx_pak++;

            //emit ratioChanged();        //每发送完成1KiB（等待ACK），发射进度条更改信号。
        }
        else{
            QByteArray sndArray= wrValue.left(197);     //发送1KiB，需要多次发送，每次最多发送197B（这是与CH9141K协商的MTU）。

            m_service->writeCharacteristic(wrChar, sndArray, QLowEnergyService::WriteWithoutResponse);
        }

        return;
    }

    if(STA_EOT_SEND == sta_ymodem){
        sta_ymodem= STA_EOT_ACK;
        LOGD("send EOT ok, ->STA_EOT_ACK");
        return;
    }


    if(STA_EOT_ACK == sta_ymodem){  //在STA_EOT_ACK中发送了全0数据包。
        sta_ymodem= STA_000_ACK;
        LOGD("send 000 ok, STA_EOT_ACK -> STA_000_ACK");
        return;
    }



    //128B的数据包。第0个包，即idx_pak==0。能一次发送完成（满足MTU）。
    if(0 == idx_pak){
        idx_pak++;
        return;
    }


    //1024B的数据包。
    wrValue= wrValue.sliced(pos);   //wrValue是1024B数据包。sliced(pos)返回的是位置pos(含）的QByteArray。(starting at position pos)。

    if(0 == wrValue.length()){
        //发送完成一个1KiB数据包。
        LOGD("%d pak(1024B) send ok", idx_pak);
        //m_ratio= (float)idx_pak;
        //m_ratio/= (float)num_pak;
        //emit ratioChanged();
        setRatio((float)idx_pak/(float)num_pak);


        idx_pak++;
    }
    else{
        //继续发送1KiB中的下一个197B。
        QByteArray sndArray= wrValue.left(197);

        m_service->writeCharacteristic(wrChar, sndArray, QLowEnergyService::WriteWithoutResponse);
    }
}

void DeviceHandler::disconnectService()
{
    m_foundHeartRateService = false;

    //disable notifications
    if (m_notificationDesc.isValid() && m_service
            && m_notificationDesc.value() == QByteArray::fromHex("0100")) {
        m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0000"));
    } else {
        if (m_control)
            m_control->disconnectFromDevice();

        delete m_service;
        m_service = nullptr;
    }
}

bool DeviceHandler::measuring() const
{
    return m_measuring;
}

bool DeviceHandler::alive() const
{
    if (simulator)
        return true;

    if (m_service)
        return m_service->state() == QLowEnergyService::RemoteServiceDiscovered;

    return false;
}

int DeviceHandler::hr() const
{
    return m_currentValue;
}

int DeviceHandler::time() const
{
    return m_start.secsTo(m_stop);
}

int DeviceHandler::maxHR() const
{
    return m_max;
}

int DeviceHandler::minHR() const
{
    return m_min;
}

int DeviceHandler::plc_addr() const
{
    return m_plc_addr;
}

void DeviceHandler::setPlc_addr(int val)
{
    m_plc_addr= val;

    emit paraplcChanged();
}

int DeviceHandler::plc_baud_idx() const
{
    return m_plc_baud_idx;
}

void DeviceHandler::setPlc_baud_idx(int val)
{
    m_plc_baud_idx= val;

    emit paraplcChanged();
}

int DeviceHandler::plc_fmt_idx() const
{
    return m_plc_fmt_idx;
}

void DeviceHandler::setPlc_fmt_idx(int val)
{
    m_plc_fmt_idx= val;

    emit paraplcChanged();
}

int DeviceHandler::plc_interval_idx() const
{
    return m_plc_interval_idx;
}

void DeviceHandler::setPlc_interval_idx(int val)
{
    m_plc_interval_idx= val;

    emit paraplcChanged();
}

int DeviceHandler::plc_protocol_idx() const
{
    return m_plc_protocol_idx;
}

void DeviceHandler::setPlc_protocol_idx(int val)
{
    m_plc_protocol_idx= val;

    emit paraplcChanged();
}

int DeviceHandler::rf_ch_idx() const
{
    return m_rf_ch_idx;
}

void DeviceHandler::setRf_ch_idx(int val)
{
    m_rf_ch_idx= val;

    emit paraplcChanged();  //必须主动发送信号。
}

int DeviceHandler::laji_gap_min_idx() const
{
    return m_laji_gap_min_idx;
}

void DeviceHandler::setLaji_gap_min_idx(int val)
{
    m_laji_gap_min_idx= val;
    emit paralajitongChanged();
}

int DeviceHandler::laji_gap_max_idx() const
{
    return m_laji_gap_max_idx;
}


void DeviceHandler::setLaji_gap_max_idx(int val)
{
    m_laji_gap_max_idx= val;
    emit paralajitongChanged();
}


int DeviceHandler::laji_hold_tmo_idx() const
{
    return m_laji_hold_tmo_idx;
}

void DeviceHandler::setLaji_hold_tmo_idx(int val)
{
    m_laji_hold_tmo_idx= val;
    emit paralajitongChanged();
    LOGD("hold_tmo_idx");
}

int DeviceHandler::lajitong_sample_cnt_idx() const
{
    return m_lajitong_sample_cnt_idx;
}

void DeviceHandler::setLajitong_sample_cnt_idx(int val)
{
    m_lajitong_sample_cnt_idx= val;
    emit paralajitongChanged();
    LOGD("lajitong_sample_cnt_idx");
}



int DeviceHandler::laji_gps_gap() const
{
    return m_laji_gps_gap;
}

void DeviceHandler::setLaji_gps_gap(int val)
{
    m_laji_gps_gap= val;
    emit paralajitongChanged();
}

int DeviceHandler::laji_zzone_val() const
{
    return m_laji_zzone_val;
}

void DeviceHandler::setLaji_zzone_val(int val)
{
    m_laji_zzone_val= val;
    emit paralajitongChanged();
}

int DeviceHandler::laji_zzone_tmo() const
{
    return m_laji_zzone_tmo;
}

void DeviceHandler::setLaji_zzone_tmo(int val)
{
    m_laji_zzone_tmo= val;
    emit paralajitongChanged();
}

int DeviceHandler::laji_curve() const
{
    return m_laji_curve;
}

void DeviceHandler::setLaji_curve(int val)
{
    m_laji_curve= val;
    emit paralajitongChanged();
}

int DeviceHandler::zero_auto_sw_idx() const
{
    return m_zero_auto_sw_idx;
}

void DeviceHandler::setZero_auto_sw_idx(int val)
{
    m_zero_auto_sw_idx= val;
    emit parasetChanged();
}

int DeviceHandler::range_zero_trace_idx() const
{
    return m_range_zero_trace_idx;
}

void DeviceHandler::setRange_zero_trace_idx(int val)
{
    m_range_zero_trace_idx= val;
    emit parasetChanged();
}

int DeviceHandler::range_stab_idx() const
{
    return m_range_stab_idx;
}

void DeviceHandler::setRange_stab_idx(int val)
{
    m_range_stab_idx= val;
    emit parasetChanged();
}

int DeviceHandler::range_zero() const
{
    return m_range_zero;
}

void DeviceHandler::setRange_zero(int val)
{
    m_range_zero= val;
    emit parasetChanged();
}

int DeviceHandler::filter_dig_idx() const
{
    return m_filter_dig_idx;
}

void DeviceHandler::setFilter_dig_idx(int val)
{
    m_filter_dig_idx= val;
    emit parasetChanged();
}

int DeviceHandler::filter_stab_idx() const
{
    return m_filter_stab_idx;
}

void DeviceHandler::setFilter_stab_idx(int val)
{
    m_filter_stab_idx= val;
    emit parasetChanged();
}

int DeviceHandler::sps_idx() const
{
    return m_sps_idx;
}

void DeviceHandler::setSps_idx(int val)
{
    m_sps_idx= val;
    emit parasetChanged();
}

int DeviceHandler::in1_define_idx() const
{
    return m_in1_define_idx;
}

void DeviceHandler::setIn1_define_idx(int val)
{
    m_in1_define_idx= val;
    emit parasetChanged();
}

int DeviceHandler::out1_define_idx() const
{
    return m_out1_define_idx;
}

void DeviceHandler::setOut1_define_idx(int val)
{
    m_out1_define_idx= val;
    emit parasetChanged();
}

int DeviceHandler::out2_define_idx() const
{
    return m_out2_define_idx;
}

void DeviceHandler::setOut2_define_idx(int val)
{
    m_out2_define_idx= val;
    emit parasetChanged();
}

int DeviceHandler::cal_pt_idx() const
{
    return m_cal_pt_idx;
}

void DeviceHandler::setCal_pt_idx(int val)
{
    m_cal_pt_idx= val;
    emit paracalChanged();
}

int DeviceHandler::cal_fd_idx() const
{
    return m_cal_fd_idx;
}

void DeviceHandler::setCal_fd_idx(int val)
{
    m_cal_fd_idx= val;
    emit paracalChanged();
}


int DeviceHandler::cal_cp() const
{
    return m_cal_cp;
}

void DeviceHandler::setCal_cp(int val)
{
    m_cal_cp= val;
    emit paracalChanged();
}

int DeviceHandler::cal_pwd() const
{
    return m_cal_pwd;
}

void DeviceHandler::setCal_pwd(int val)
{
    m_cal_pwd= val;
    emit pwdChanged();
}

int DeviceHandler::cal_t() const
{
    return m_cal_t;
}

void DeviceHandler::setCal_t(int val)
{
    m_cal_t= val;
    emit paracalChanged();
}


int DeviceHandler::cal_load1() const
{
    return m_cal_load1;
}

void DeviceHandler::setCal_load1(int val)
{
    m_cal_load1= val;
    emit paracalChanged();
}

int DeviceHandler::cal_load2() const
{
    return m_cal_load2;
}

void DeviceHandler::setCal_load2(int val)
{
    m_cal_load2= val;
    emit paracalChanged();
}

int DeviceHandler::cal_load3() const
{
    return m_cal_load3;
}

void DeviceHandler::setCal_load3(int val)
{
    m_cal_load3= val;
    emit paracalChanged();
}


int DeviceHandler::cal_load4() const
{
    return m_cal_load4;
}

void DeviceHandler::setCal_load4(int val)
{
    m_cal_load4= val;
    emit paracalChanged();
}

QString DeviceHandler::txt_color() const
{
    return m_txt_color;
}

void DeviceHandler::setTxt_color(QString color)
{
    m_txt_color= color;
    //emit txt_ok_vChanged();
}

QString DeviceHandler::txt_info() const
{
    return m_txt_info;
}

void DeviceHandler::setTxt_info(QString name)
{
    m_txt_info= name;
    //emit txt_ok_vChanged();
}

bool DeviceHandler::txt_ok_v() const
{
    return m_txt_ok_v;
}

void DeviceHandler::setTxt_ok_v(bool val)
{
    m_txt_ok_v= val;
    emit txt_ok_vChanged();
}

bool DeviceHandler::v_txt_info_cal() const
{
    return m_v_txt_info_cal;
}

void DeviceHandler::setV_txt_info_cal(bool val)
{
    m_v_txt_info_cal= val;
    emit txt_ok_vChanged();
}

bool DeviceHandler::v_txt_info_key() const
{
    return m_v_txt_info_key;
}

void DeviceHandler::setV_txt_info_key(bool val)
{
    m_v_txt_info_key= val;
    emit txt_ok_vChanged();
}

bool DeviceHandler::v_txt_info_set() const
{
    return m_v_txt_info_set;
}

void DeviceHandler::setV_txt_info_set(bool val)
{
    m_v_txt_info_set= val;
    emit txt_ok_vChanged();
}

bool DeviceHandler::v_txt_info_plc() const
{
    return m_v_txt_info_plc;
}

void DeviceHandler::setV_txt_info_plc(bool val)
{
    m_v_txt_info_plc= val;
    emit txt_ok_vChanged();
}

bool DeviceHandler::v_txt_info_laji() const
{
    return m_v_txt_info_laji;
}

void DeviceHandler::setV_txt_info_laji(bool val)
{
    m_v_txt_info_laji= val;
    emit txt_ok_vChanged();
}

bool DeviceHandler::pwd_ok() const
{
    return m_pwd_ok;
}

void DeviceHandler::setPwd_ok(bool val)
{
    m_pwd_ok= val;
    emit pwd_okChanged();
}

bool DeviceHandler::btn_update() const
{
    return m_btn_update;
}

void DeviceHandler::setBtn_update(bool val)
{
    m_btn_update= val;
    emit btn_updateChanged();
}

bool DeviceHandler::stat_btn_getfile() const
{
    return m_stat_btn_getfile;
}

void DeviceHandler::setStat_btn_getfile(bool val)
{
    m_stat_btn_getfile= val;
    emit stat_btn_getfileChanged();
}

bool DeviceHandler::stat_tim() const
{
    return m_stat_tim;
}

void DeviceHandler::setStat_tim(bool val)
{
    m_stat_tim= val;
    emit stat_timChanged();
}

void DeviceHandler::updateParaUI()
{
    emit parasetChanged();          //ParaSet.qml下关联的属性。
    emit paraplcChanged();          //ParaPlc.qml下关联的属性。
    emit paralajitongChanged();         //ParaLaji.qml下关联的属性。
}


bool DeviceHandler::update() const
{
    return m_update;
}

void DeviceHandler::setUpdate(bool val)
{
    m_update= val;
    emit update_okChanged();
}


float DeviceHandler::ratio() const
{
    return m_ratio;
}

float DeviceHandler::average() const
{
    return m_avg;
}

//QByteArray DeviceHandler::wrValue() const
//{
//    return m_wrValue;
//}

float DeviceHandler::calories() const
{
    return m_calories;
}

QString DeviceHandler::bin_name() const
{
    return m_binfilename;
}

void DeviceHandler::setBin_name(QString name)
{
    m_binfilename= name;
    emit bin_nameChanged();
}


QString DeviceHandler::ver_info() const
{
    return m_ver_info;
}

void DeviceHandler::setVer_info(QString name)
{
    m_ver_info= name;
    emit ver_infoChanged();
}


QString DeviceHandler::ver_info_2() const
{
    return m_ver_info_2;
}

void DeviceHandler::setVer_info_2(QString name)
{
    m_ver_info_2= name;
    emit ver_info_2Changed();
}


QString DeviceHandler::ver_boot() const
{
    return m_ver_boot;
}

void DeviceHandler::setVer_boot(QString name)
{
    m_ver_boot= name;
    emit ver_bootChanged();
}


void DeviceHandler::addMeasurement(int value)
{
    m_currentValue = value;

    // If measuring and value is appropriate
    if (m_measuring && value > 30 && value < 250) {

        m_stop = QDateTime::currentDateTime();
        m_measurements << value;

        m_min = m_min == 0 ? value : qMin(value, m_min);
        m_max = qMax(value, m_max);
        m_sum += value;
        m_avg = (double)m_sum / m_measurements.size();
        m_calories = ((-55.0969 + (0.6309 * m_avg) + (0.1988 * 94) + (0.2017 * 24)) / 4.184) * 60 * time()/3600;
    }

    emit statsChanged();
}


/*向外设发送按键值（以Modbus协议06h写单个寄存器）
 * 说明：参考startMeasurment()
*/
void DeviceHandler::key_to_periph(uint16_t val)
{
    //WriteNoResponse.
    if (m_service) {
        quint16 uuid_wr= 0xfff2;    //Write no response属性。
        //const QLowEnergyCharacteristic wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));
        wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));

        if (!wrChar.isValid()) {
            LOGD("err:no wrChar");

            setTxt_color("red");
            setTxt_info("蓝牙连接已断开");
            setTxt_ok_v(true);          //显示提示信息。
//            setTxt_info("蓝牙连接已断开");
//            setV_txt_info_cal(true);   //显示提示信息。
//            setV_txt_info_key(true);
//            setV_txt_info_set(true);
//            setV_txt_info_plc(true);
//            setV_txt_info_laji(true);
        }
        else{
            sta_ymodem= STA_KEY;
            m_addr= 0;      //虚拟按键只有一个地址，但有很多按键值。

            wrValue.clear();
            wrValue.append(0x01);
            wrValue.append(0x06);       //功能码：写单个寄存器。
            wrValue.append((char)0x0);  //寄存器地址
            wrValue.append((char)0x0);
            wrValue.append(val>> 8);    //寄存器数值,MSB
            wrValue.append(val);        //寄存器数值,LSB
            uint16_t crc16= ModbusCRC16(wrValue);
            wrValue.append(crc16);       //CRC16，LSB first.
            wrValue.append(crc16>> 8);

            LOGD("%02x-%02x-%02x-%02x-%02x-%02x-%02x", wrValue.at(0),wrValue.at(1),wrValue.at(2),wrValue.at(3),wrValue.at(4),wrValue.at(5),wrValue.at(6));

            m_service->writeCharacteristic(wrChar, wrValue, QLowEnergyService::WriteWithoutResponse);
        }
    }
    else{
        LOGD("err:service invalid");

        setTxt_color("red");
        setTxt_info("蓝牙未连接");
        setTxt_ok_v(true);          //显示提示信息
    }
}



/*向外设发送按键值（以Modbus协议06h写单个寄存器）
 * 说明：参考startMeasurment()
*/
void DeviceHandler::data10h_to_periph(uint16_t addr, uint32_t val)
{
    //WriteNoResponse.
    if (m_service) {
        quint16 uuid_wr= 0xfff2;    //Write no response属性。
        //const QLowEnergyCharacteristic wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));
        wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));

        if (!wrChar.isValid()) {
            LOGD("err:no wrChar");

            setTxt_color("red");
            setTxt_info("蓝牙连接已断开");
            setTxt_ok_v(true);          //显示提示信息。
//            setTxt_info("蓝牙连接已断开");
//            setV_txt_info_cal(true);   //显示提示信息。
//            setV_txt_info_key(true);
//            setV_txt_info_set(true);
//            setV_txt_info_plc(true);
//            setV_txt_info_laji(true);
        }
        else{
            sta_ymodem= STA_10H;
            m_addr= addr;
            m_val32= val;

            /*
            */
            wrValue.clear();
            wrValue.append(0x01);
            wrValue.append(0x10);       //功能码：写单个寄存器。
            wrValue.append(addr>>8);    //寄存器地址
            wrValue.append(addr);
            wrValue.append((char)0);    //寄存器个数:2
            wrValue.append(2);
            wrValue.append(4);          //字节数：4
            wrValue.append(val>>24);    //寄存器值,MSB
            wrValue.append(val>>16);    //寄存器值
            wrValue.append(val>>8);     //寄存器值
            wrValue.append(val);        //寄存器值
            uint16_t crc16= ModbusCRC16(wrValue);
            wrValue.append(crc16);       //CRC16，LSB first.
            wrValue.append(crc16>> 8);

            LOGD("%02x-%02x-%02x-%02x=%02x-%02x-%02x-%02x", wrValue.at(0),wrValue.at(1),wrValue.at(2),wrValue.at(3),wrValue.at(7),wrValue.at(8),wrValue.at(9),wrValue.at(10));

            m_service->writeCharacteristic(wrChar, wrValue, QLowEnergyService::WriteWithoutResponse);
        }
    }
    else{
        LOGD("err:service invalid");

        setTxt_color("red");
        setTxt_info("蓝牙未连接");
        setTxt_ok_v(true);          //显示提示信息
    }
}



/*向外设发送DTU的地址和端口。
 * 说明：参考startMeasurment()
*/
void DeviceHandler::data10h_to_periph2(uint16_t addr, uint16_t ip1,uint16_t ip2,uint16_t ip3,uint16_t ip4, uint16_t port)
{
    //WriteNoResponse.
    if (m_service) {
        quint16 uuid_wr= 0xfff2;    //Write no response属性。
        //const QLowEnergyCharacteristic wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));
        wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));

        if (!wrChar.isValid()) {
            LOGD("err:no wrChar");

            setTxt_color("red");
            setTxt_info("蓝牙连接已断开");
            setTxt_ok_v(true);          //显示提示信息。
//            setTxt_info("蓝牙连接已断开");
//            setV_txt_info_cal(true);   //显示提示信息。
//            setV_txt_info_key(true);
//            setV_txt_info_set(true);
//            setV_txt_info_plc(true);
//            setV_txt_info_laji(true);
        }
        else{
            sta_ymodem= STA_10H;
            m_addr= addr;
            //m_val32= val;

            /*
            */
            wrValue.clear();
            wrValue.append(0x01);
            wrValue.append(0x10);       //功能码：写单个寄存器。
            wrValue.append(addr>>8);    //寄存器地址
            wrValue.append(addr);
            wrValue.append((char)0);    //寄存器个数:5
            wrValue.append(5);

            wrValue.append(10);         //字节数：2*5

            wrValue.append(ip1>>8);     //寄存器值,MSB
            wrValue.append(ip1);        //寄存器值
            wrValue.append(ip2>>8);     //寄存器值,MSB
            wrValue.append(ip2);        //寄存器值
            wrValue.append(ip3>>8);     //寄存器值,MSB
            wrValue.append(ip3);        //寄存器值
            wrValue.append(ip4>>8);     //寄存器值,MSB
            wrValue.append(ip4);        //寄存器值
            wrValue.append(port>>8);    //寄存器值
            wrValue.append(port);       //寄存器值
            uint16_t crc16= ModbusCRC16(wrValue);
            wrValue.append(crc16);       //CRC16，LSB first.
            wrValue.append(crc16>> 8);

            LOGD("%02x-%02x-%02x-%02x=%02x-%02x-%02x-%02x,%02x%02x", wrValue.at(0),wrValue.at(1),wrValue.at(2),wrValue.at(3),wrValue.at(8),wrValue.at(10),wrValue.at(12),wrValue.at(14), wrValue.at(15),wrValue.at(16));

            m_service->writeCharacteristic(wrChar, wrValue, QLowEnergyService::WriteWithoutResponse);
        }
    }
    else{
        LOGD("err:service invalid");

        setTxt_color("red");
        setTxt_info("蓝牙未连接");
        setTxt_ok_v(true);          //显示提示信息
    }
}

/*向设备发送03h，以请求双保持寄存器值。
*/
void DeviceHandler::data03h_to_periph(uint16_t addr, uint32_t cnt)
{
    if (m_service) {
        quint16 uuid_wr= 0xfff2;    //Write no response属性。
        //const QLowEnergyCharacteristic wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));
        wrChar = m_service->characteristic(QBluetoothUuid(uuid_wr));

        if (!wrChar.isValid()) {
            LOGD("err:no wrChar");

            setTxt_color("red");
            setTxt_info("蓝牙连接已断开");
            setTxt_ok_v(true);          //显示提示信息。
//            setTxt_info("蓝牙连接已断开");
//            setV_txt_info_cal(true);   //显示提示信息。
//            setV_txt_info_key(true);
//            setV_txt_info_set(true);
//            setV_txt_info_plc(true);
//            setV_txt_info_laji(true);
        }
        else{
            sta_ymodem= STA_03H;
            m_addr= addr;

            //填充数据。
            wrValue.clear();
            wrValue.append(0x01);
            wrValue.append(0x03);       //功能码：写单个寄存器。
            wrValue.append(addr>>8);    //寄存器地址
            wrValue.append(addr);
            if(25 == cnt){
                wrValue.append((char)0);    //寄存器个数:25.
                wrValue.append(25);
            }
            else{
                wrValue.append((char)0);    //寄存器个数:2
                wrValue.append(2);
            }

            uint16_t crc16= ModbusCRC16(wrValue);
            wrValue.append(crc16);       //CRC16，LSB first.
            wrValue.append(crc16>> 8);

            LOGD("%02x-%02x-%02x-%02x=%02x-%02x", wrValue.at(0),wrValue.at(1),wrValue.at(2),wrValue.at(3),wrValue.at(4),wrValue.at(5));

            m_service->writeCharacteristic(wrChar, wrValue, QLowEnergyService::WriteWithoutResponse);
        }
    }
    else{
        LOGD("err:service invalid");

        setTxt_color("red");
        setTxt_info("蓝牙未连接");
        setTxt_ok_v(true);          //显示提示信息
    }
}


/*在C++中清空QML中BOOT界面的版本信息。
*/
void DeviceHandler::reset_ver_info()
{
    setVer_info("");
    setVer_info_2("");
    setVer_boot("");
}


//将client操作过程的提示信息，显示到界面。
void DeviceHandler::client_err(QString err)
{
    setBin_name(err);   //在文件名输入框中显示错误信息。
    setStat_btn_getfile(true);  //传输失败，需要使能“获取文件”按钮，以允许人为重试。
}

//将client操作结果，显示到界面。
void DeviceHandler::client_trans_ok(QString name)
{
    setBin_name(name);
    setBtn_update(true);
    //setStat_btn_getfile(true);    //传输成功，不需要使能“获取文件”按钮。
}


void DeviceHandler::connectY()
{
    qDebug()<<"connect ok XXX";
}

/*在特定目录"/sdcard/DT45/"下查找.bin文件。
*/
void DeviceHandler::boot_findbin(uint16_t ip1,uint16_t ip2,uint16_t ip3,uint16_t ip4, uint16_t port)
{
//    QStringList file_list;
//    QDir binDir("/sdcard/DT45G");   //".bin", QDir::Name, QDir::Files. !!已验证，目录"/sdcard/DT45GXX"不存在，目录"/sdcard/DT45G"存在。

//    if(binDir.exists()){
//        LOGD("/sdcard/DT45G ok");
//    }
//    else{
//        LOGD("err:/sdcard/DT45G");
//    }

//    //在旧honor手机，file_list()一直为0。原因还是应用程序无存储权限。
//    file_list= binDir.entryList({"*.bin"}, QDir::Files, QDir::NoSort);//QDir::AllEntries, QDir::NoSort);  //QDir::Files
//    qDebug()<< "files num="<<file_list.size();
////    qDebug()<< "file_1"<< file_list.value(0);
////    qDebug()<< "file_2" << file_list.value(1);
////    qDebug()<< "file_3" << file_list.value(2);
////    qDebug()<< "file_4" << file_list.value(3);
//    qDebug()<< "files" << file_list;

////    qDebug()<<"xxx" <<binDir.path();
////    qDebug()<< "abs path:"<< binDir.absolutePath();
////    qDebug()<<"dir name"<< binDir.dirName();

//    if(file_list.size()){
//       //有1个或多个.bin文件时，取第1个.bin文件。
//       setBin_name(file_list.value(0));
//    }

    setTxt_ok_v(false);
    setBin_name("");    //清空文件名框。
    setRatio(0.0);      //清空进度条。
    //setStat_btn_getfile(false);

    /*将界面中的服务器IP和端口，传入client。
    */
    qDebug()<<"set ip&port";
    QString ip= QString("%1.%2.%3.%4").arg(ip1).arg(ip2).arg(ip3).arg(ip4);
    client->setIP(ip);
    client->setPort(port);

    client->startX(); //执行client线程。
}



//CRC16计算函数
uint16_t ModbusCRC16(QByteArray senddata)
{
    int len=senddata.size();
    uint16_t wcrc=0XFFFF;//预置16位crc寄存器，初值全部为1
    uint8_t temp;//定义中间变量
    int i=0,j=0;//定义计数
    for(i=0;i<len;i++)//循环计算每个数据
    {
       temp=senddata.at(i);
       wcrc^=temp;
       for(j=0;j<8;j++){
          //判断右移出的是不是1，如果是1则与多项式进行异或。
          if(wcrc&0X0001){
              wcrc>>=1;//先将数据右移一位
              wcrc^=0XA001;//与上面的多项式进行异或
          }
          else//如果不是1，则直接移出
              wcrc>>=1;//直接移出
       }
    }

    temp=wcrc;//crc的值
    return wcrc;
}
